/**
 * 
 */
package org.vsg.stock.basic.service.handler;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.vsg.stock.basic.StockHelper;
import org.vsg.stock.basic.domain.StockDayIndicator;
import org.vsg.stock.basic.vo.TickChangeVO;
import org.vsg.stock.core.domain.StockTick;

/**
 * @author vison
 *
 */
public class BigOrderTradeTickHandler implements TradeTickNatureHandler {

	private DecimalFormat decFormat = new DecimalFormat("##0.000");		
	
	/**
	 * set the logger for developer code , do not use System.print(ln) to out print message.
	 */
	private static final Logger logger = LoggerFactory.getLogger(BigOrderTradeTickHandler.class);
	
	
	private long vol = 0;
	
	private double amount = 0;
	
	private StockHelper inst  = StockHelper.getInstance();
	/* (non-Javadoc)
	 * @see org.vsg.stock.basic.service.handler.TradeTickNatureHandler#handle(java.util.Map, org.vsg.stock.basic.domain.StockDayIndicator, java.util.List, int, double, double)
	 */
	@Override
	public short handle(StockDayIndicator indicator, List<StockTick> stockTickList,
			int index, double bigOrdLevel, double midBigOrdLevel) {
		// TODO Auto-generated method stub
		StockTick st = stockTickList.get(index);
		StockTick lastSt = stockTickList.get(index-1);
		StockTick nextSt = stockTickList.get(index+1);
		
		// --- check the big order level ---
		long returnVol = 0;
		double returnAmount = 0;
		if (st.getPresentTickVol() >= bigOrdLevel) {
			//
			TickChangeVO tcVO = compareTickChange(st, lastSt , bigOrdLevel);
			List<Double> disappearPriceList = tcVO.getDisappearPrice();
			
			for (Iterator<Double> disappearPriceIter = disappearPriceList.iterator() ; disappearPriceIter.hasNext(); ) {
				double checkPrice = disappearPriceIter.next();
				short posi = inst.getCurrentPricePosition( checkPrice , lastSt);
				long vol = calculateMatchOrderVol( checkPrice , posi , stockTickList , index , bigOrdLevel);

				returnVol+=vol;
				returnAmount+=vol * checkPrice * 100;
			}
		}
		
		amount = Double.parseDouble( decFormat.format( returnAmount ) );
		vol = returnVol;

		if (vol != 0) {
			return TradeTickNatureHandler.MATCH;
		}		
		
		return TradeTickNatureHandler.SKIP;
	}

	
	
	
	
	@Override
	public double getAmount() {
		// TODO Auto-generated method stub
		return this.amount;
	}





	@Override
	public long getVol() {
		// TODO Auto-generated method stub
		return this.vol;
	}





	private long calculateMatchOrderVol(double price , short currentPostion , List<StockTick> stockTickList , int index ,double ordLevel) {
		
		List<Long> changeHisList = new ArrayList<Long>();
		StockTick st = null;
		Map<Double,Long> preMap = null;
		long lastVol = 0;
		for (int i = index-1 ; i > 0 ; i--) {
			st = stockTickList.get(i);			
			preMap = new HashMap<Double,Long>();
			preMap.put( st.getConsignedSell5Price() , st.getConsignedSell5Vol() );
			preMap.put( st.getConsignedSell4Price() , st.getConsignedSell4Vol());
			preMap.put( st.getConsignedSell3Price() , st.getConsignedSell3Vol() );
			preMap.put( st.getConsignedSell2Price() , st.getConsignedSell2Vol());
			preMap.put( st.getConsignedSell1Price() , st.getConsignedSell1Vol());
		
			preMap.put( st.getConsignedBuy1Price() , -st.getConsignedBuy1Vol() );
			preMap.put( st.getConsignedBuy2Price() , -st.getConsignedBuy2Vol());
			preMap.put( st.getConsignedBuy3Price() , -st.getConsignedBuy3Vol() );
			preMap.put( st.getConsignedBuy4Price() , -st.getConsignedBuy4Vol());
			preMap.put( st.getConsignedBuy5Price() , -st.getConsignedBuy5Vol());
			
			// --- handle logic ----
			if (currentPostion == StockHelper.VISIBLE_AT_BUY_PRICE ) {

				short posi = inst.getCurrentPricePosition( price , st);
				if (posi > StockHelper.AT_TRADING_PRICE) {
					lastVol = 0;
					break;
				} else {
					Long vol = preMap.get(price);
					if (vol != null) {
						long diffVol = vol - lastVol;
						if (Math.abs(diffVol) > ordLevel) {
							changeHisList.add( diffVol );
						}
						lastVol = vol;						
					}
				}
			}				
			
			else if (currentPostion == StockHelper.VISIBLE_AT_SELL_PRICE ) {

				short posi = inst.getCurrentPricePosition( price , st);
				if (posi < StockHelper.AT_TRADING_PRICE) {
					lastVol = 0;
					break;
				} else {
					Long vol = preMap.get(price);
					if (vol != null) {
						long diffVol = vol - lastVol;
						if (Math.abs(diffVol) > ordLevel) {
							changeHisList.add( diffVol );
						}
						lastVol = vol;						
					}
				}
			}				
		}
		
		long result = 0;
		// --- update change history list ---
		if (changeHisList.size() > 1) {
			for (int i = 1 ; i < changeHisList.size() ;i++ ) {
				result+=changeHisList.get(i);
			}
			
		} else if (changeHisList.size() == 1) {
			result = (long)Math.abs( changeHisList.get(0) * 0.5 );
		}
		
		
		
		return result;
	}

	
	private TickChangeVO compareTickChange(StockTick currentSt , StockTick preSt , double orderLevel) {

		Set<Double> preAllPrice = new HashSet<Double>();
		
		Map<Double,Long> preMap = new HashMap<Double,Long>();		
		preMap.put( preSt.getConsignedSell5Price() , preSt.getConsignedSell5Vol() );
		preMap.put( preSt.getConsignedSell4Price() , preSt.getConsignedSell4Vol());
		preMap.put( preSt.getConsignedSell3Price() , preSt.getConsignedSell3Vol() );
		preMap.put( preSt.getConsignedSell2Price() , preSt.getConsignedSell2Vol());
		preMap.put( preSt.getConsignedSell1Price() , preSt.getConsignedSell1Vol());
	
		preMap.put( preSt.getConsignedBuy1Price() , -preSt.getConsignedBuy1Vol() );
		preMap.put( preSt.getConsignedBuy2Price() , -preSt.getConsignedBuy2Vol());
		preMap.put( preSt.getConsignedBuy3Price() , -preSt.getConsignedBuy3Vol() );
		preMap.put( preSt.getConsignedBuy4Price() , -preSt.getConsignedBuy4Vol());
		preMap.put( preSt.getConsignedBuy5Price() , -preSt.getConsignedBuy5Vol());
		
		Map<Double,Long> currentMap = new HashMap<Double,Long>();		
		currentMap.put( currentSt.getConsignedSell5Price() , currentSt.getConsignedSell5Vol() );
		currentMap.put( currentSt.getConsignedSell4Price() , currentSt.getConsignedSell4Vol());
		currentMap.put( currentSt.getConsignedSell3Price() , currentSt.getConsignedSell3Vol() );
		currentMap.put( currentSt.getConsignedSell2Price() , currentSt.getConsignedSell2Vol());
		currentMap.put( currentSt.getConsignedSell1Price() , currentSt.getConsignedSell1Vol());
	
		currentMap.put( currentSt.getConsignedBuy1Price() , -currentSt.getConsignedBuy1Vol() );
		currentMap.put( currentSt.getConsignedBuy2Price() , -currentSt.getConsignedBuy2Vol());
		currentMap.put( currentSt.getConsignedBuy3Price() , -currentSt.getConsignedBuy3Vol() );
		currentMap.put( currentSt.getConsignedBuy4Price() , -currentSt.getConsignedBuy4Vol());
		currentMap.put( currentSt.getConsignedBuy5Price() , -currentSt.getConsignedBuy5Vol());

		
		Set<Double> allPrice = currentMap.keySet();
		
		TickChangeVO vo = new TickChangeVO();
		
		double level = 0.19;
		
		List<Double> preKeySet = new ArrayList<Double>(preMap.keySet());
		Collections.sort(preKeySet);
		
		Double prePrice = null;
		for (Iterator<Double> preKeySetIter = preKeySet.iterator(); preKeySetIter.hasNext(); ) {
			prePrice = preKeySetIter.next();
			preAllPrice.add(prePrice);
			long preVol = preMap.get( prePrice );
			if (Math.abs( preVol ) >= orderLevel) {
				if (allPrice.contains( prePrice )) {
					Long vol = currentMap.get( prePrice);
					double diffVol = Math.abs( preVol - vol );
					double diffBalance = (vol == 0 || diffVol == 0) ? 0: Math.abs( vol ) / diffVol;
					if ( diffBalance < level && diffBalance > 0) {
						vo.addDisappearPrice( prePrice );
					} 
				} else {
					short prePosition = inst.getCurrentPricePosition( prePrice , preSt);
					short currentPosition = inst.getCurrentPricePosition( prePrice , currentSt);
					
					if ( (prePosition == StockHelper.VISIBLE_AT_SELL_PRICE 
						&& currentPosition != StockHelper.OVER_SELL_SHOWN_PRICE )
						|| (prePosition == StockHelper.VISIBLE_AT_BUY_PRICE
						&& currentPosition != StockHelper.UNDER_BUY_SHOWN_PRICE	)
					) {
						vo.addDisappearPrice( prePrice );
					}
					
				}
			
			}
		}

		
		
		// --- add new price ---
		Double newPrice = null;
		for (Iterator<Double> newPriIter = allPrice.iterator() ; newPriIter.hasNext();) {
			newPrice = newPriIter.next();
			if (!preAllPrice.contains( newPrice)) {
				vo.addNewAppearPrice(newPrice);
			}
		}
		
		return vo;
	
	}

}
